package com.ycxh.controller;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ycxh.controller.dto.DirConfig;
import com.ycxh.util.ErrorMessageEnum;
import com.ycxh.util.FileUtil;
import com.ycxh.util.ViewResult;
import com.ycxh.util.ZipUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/file/")
public class FileController {

    private static Logger logger = LoggerFactory.getLogger(FileController.class);

    // 上传文件保存目录
    @Value("${file.store.dir:/}")
    private String fileStoreRootDir;

    // 文件下载前缀
    @Value("${file.download.url:}")
    private String fileDownloadUrl;

    // 允许解压的目录配置文件名称
    private final static String DIR_CONFIG_FILE_NAME = "dir.json";

    /**
     * 上传zip包，并解压至服务器目录
     *
     * @param request
     * @param file    zip文件
     * @param path    解压目录
     */
    @RequestMapping(value = "deployZip", method = RequestMethod.POST)
    @ResponseBody
    public String deployZip(HttpServletRequest request, @RequestParam("file") MultipartFile file, @RequestParam("path") String path) throws Exception {
        ViewResult viewResult = ViewResult.newInstance();
        if (file == null) {
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg("文件不能为空");
            return viewResult.json();
        }
        if (StringUtils.isBlank(path)) {
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg("解压路径不能为空");
            return viewResult.json();
        }
        String originalFilename = file.getOriginalFilename();
        logger.info("原文件名称：" + originalFilename);
        if (StringUtils.isBlank(originalFilename) || !"zip".equalsIgnoreCase(FileUtil.getFileType(originalFilename))) {
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg("只支持zip格式");
            return viewResult.json();
        }

        // 获取允许解压的目录
        List<DirConfig> list = getDirConfig();
        if (list != null && list.size() > 0) {
            List<String> pathList = list.stream().map(DirConfig::getPath).collect(Collectors.toList());
            if (!pathList.contains(path)) {
                viewResult.code(ErrorMessageEnum.失败.getCode()).msg("无操作权限");
                return viewResult.json();
            }
        }

        try (InputStream inputStream = file.getInputStream()) {

            ZipUtil zip = new ZipUtil();
            boolean res = zip.unzipByInputStream(inputStream, path);
            if (res) {
                viewResult.code(ErrorMessageEnum.成功.getCode()).msg(ErrorMessageEnum.成功.getMsg());
            } else {
                viewResult.code(ErrorMessageEnum.失败.getCode()).msg(ErrorMessageEnum.失败.getMsg());
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg(ErrorMessageEnum.失败.getMsg());
        }

        return viewResult.json();
    }


    /**
     * 获取允许解压的目录
     * Springboot读取resources下的json配置文件 https://wenku.baidu.com/view/e6a039e05bf5f61fb7360b4c2e3f5727a5e92423.html
     */
    public List<DirConfig> getDirConfig() throws Exception {
        String jsonString = readJSON(DIR_CONFIG_FILE_NAME);
        if (StringUtils.isNotBlank(jsonString)) {
            // 创建Gson对象
            Gson gson = new Gson();
            List<DirConfig> list = gson.fromJson(jsonString, new TypeToken<List<DirConfig>>() {
            }.getType());
            return list;
        }
        return null;
    }

    /**
     * 读取json文件
     * https://segmentfault.com/a/1190000040311888
     */
    private String readJSON(String fileName) throws Exception {
        String result = null;
        try (InputStream fileInputStream = this.getClass().getResourceAsStream("/" + DIR_CONFIG_FILE_NAME);
             BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));
        ) {
            StringBuilder sb = new StringBuilder();
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            // result = sb.toString().replaceAll("\r\n", "").replaceAll(" +", "");
            result = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 上传文件
     */
    @RequestMapping(value = "uploadFile", method = RequestMethod.POST)
    @ResponseBody
    public String uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
        ViewResult viewResult = ViewResult.newInstance();
        if (file == null) {
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg("文件不能为空");
            return viewResult.json();
        }
        String originalFilename = file.getOriginalFilename();
        logger.info("原文件名称：" + originalFilename);
        originalFilename = originalFilename.substring(originalFilename.lastIndexOf('\\') + 1);

        // 扩展名
        String fileExtName = FileUtil.getFileType(originalFilename);

        if (StringUtils.isBlank(originalFilename) || !"zip".equalsIgnoreCase(fileExtName)) {
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg("只支持zip格式");
            return viewResult.json();
        }
        // 去掉文件扩展名
        fileExtName = "." + fileExtName;
        // originalFilename.replace(fileExtName, System.currentTimeMillis() + fileExtName);
        int endIndex = originalFilename.length() - fileExtName.length();
        if (endIndex < 1) {
            viewResult.code(ErrorMessageEnum.失败.getCode()).msg("文件名异常");
            return viewResult.json();
        }
        String filename = originalFilename.substring(0, endIndex) + "—" + System.currentTimeMillis() + fileExtName;

        File destFile = new File(fileStoreRootDir, filename);
        File pFile = destFile.getParentFile();
        if (!pFile.exists()) {
            pFile.mkdirs();
        }

        try (FileOutputStream output = new FileOutputStream(destFile)) {
            byte buff[] = new byte[1024 * 10];
            InputStream input = file.getInputStream();

            int i;
            while ((i = input.read(buff)) != -1) {
                output.write(buff, 0, i);
            }

            // 返回下载链接
            viewResult.code(ErrorMessageEnum.成功.getCode()).msg(ErrorMessageEnum.成功.getMsg())
                    .put("downUrl", StringUtils.isBlank(fileDownloadUrl) ? null : fileDownloadUrl + destFile.getName());
            return viewResult.json();
        } catch (Exception e) {
            logger.error("保存上传文件异常");
            logger.error(e.getMessage(), e);
        }

        viewResult.code(ErrorMessageEnum.失败.getCode()).msg(ErrorMessageEnum.失败.getMsg());
        return viewResult.json();
    }

    /**
     * 下载文件
     * SpringMvc中@PathVariable出现点号“.”;时导致路径参数不全的问题解决办法 https://blog.csdn.net/u011110968/article/details/80163759
     */
    @RequestMapping(value = "downloadFile/{filePath:.+}", method = RequestMethod.GET)
    @ResponseBody
    public void downloadFile(@PathVariable("filePath") String filePath, HttpServletRequest request,
                             HttpServletResponse response) throws Exception {
        response.setCharacterEncoding("UTF-8");

        if (StringUtils.isBlank(filePath)) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }

        File downloadFile = new File(fileStoreRootDir, filePath);

        if (!downloadFile.exists()) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }

        responseFile(downloadFile, filePath, request, response);
    }

    public static void responseFile(File downloadFile, String outputFileName, HttpServletRequest request, HttpServletResponse response) {
        ServletContext context = request.getServletContext();
        // 获取文件类型
        String mimeType = context.getMimeType(downloadFile.getAbsolutePath());
        if (mimeType == null) {
            mimeType = "application/octet-stream";
        }

        response.setContentType(mimeType);
        String headerValue = null;
        try {
            // 解决Firefox浏览器中文件名中文乱码
            // https://blog.csdn.net/Jon_Smoke/article/details/53699400
            headerValue = String.format("attachment; filename* = UTF-8''%s", URLEncoder.encode(outputFileName, "UTF-8"));
        } catch (Exception e) {
            headerValue = String.format("attachment; filename=\"%s\"", outputFileName);
            logger.error(e.getMessage(), e);
        }

        response.setHeader("Content-Disposition", headerValue);

        // 断点续传
        response.setHeader("Accept-Ranges", "bytes");
        long downloadSize = downloadFile.length();
        long fromPos = 0, toPos = 0;
        if (request.getHeader("Range") == null) {
            response.setHeader("Content-Length", downloadSize + "");
        } else {
            // 若客户端传来Range，说明之前下载了一部分，设置206状态(SC_PARTIAL_CONTENT)
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            String range = request.getHeader("Range");
            String bytes = range.replaceAll("bytes=", "");
            String[] ary = bytes.split("-");
            fromPos = Long.parseLong(ary[0]);
            if (ary.length == 2) {
                toPos = Long.parseLong(ary[1]);
            }
            int size;
            if (toPos > fromPos) {
                size = (int) (toPos - fromPos);
            } else {
                size = (int) (downloadSize - fromPos);
            }
            response.setHeader("Content-Length", size + "");
            downloadSize = size;
        }

        try (RandomAccessFile in = new RandomAccessFile(downloadFile, "r");
             OutputStream out = response.getOutputStream();) {
            // 设置下载起始位置
            if (fromPos > 0) {
                in.seek(fromPos);
            }
            // 缓冲区大小
            int bufLen = (int) (downloadSize < 2048 ? downloadSize : 2048);
            byte[] buffer = new byte[bufLen];
            int num;
            // 当前写到客户端的大小
            int count = 0;

            while ((num = in.read(buffer)) != -1) {
                out.write(buffer, 0, num);
                count += num;
                // 处理最后一段，计算不满缓冲区的大小
                if (downloadSize - count < bufLen) {
                    bufLen = (int) (downloadSize - count);
                    if (bufLen == 0) {
                        break;
                    }
                    buffer = new byte[bufLen];
                }
            }
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String filename = "1234567.zip";
        String fileExtName = FileUtil.getFileType(filename);
        System.out.println(filename.substring(0, filename.length() - fileExtName.length() - 1));
        // 不能为负数
        // Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -3
        System.out.println(filename.substring(-fileExtName.length()));
    }

}
